package com.ys.utils.base.j2se;


import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.sql.Timestamp;
import java.util.Date;
import java.util.HashMap;

@SuppressWarnings({ "rawtypes", "unchecked" })
public class PropertyFieldUtils {
	private HashMap classprops;

//	private Class<?> objclass;

	private Object objInstance;
	private static HashMap propCache = new HashMap();


	public PropertyFieldUtils(Class c) {
//		this.objclass = c;
		this.classprops = ((HashMap) propCache.get(c));
		if (this.classprops == null) {
			this.classprops = getClassPropertys(c);
			propCache.put(c, this.classprops);
		}
	}

	public PropertyFieldUtils(Object obj) {
		this(obj.getClass());
		this.objInstance = obj;
	}

	public void setInstance(Object obj) {
		this.objInstance = obj;
	}

	private PropertyField getPropertyFieldDefine(String name) throws Exception {
		PropertyFieldDescriptor desc = (PropertyFieldDescriptor) this.classprops.get(name);

		Object obj = getInstance();

		if (desc == null) {
			String[] propertyNames = name.split("\\.");
			if (propertyNames.length > 1) {
				for (int i = 0; i < propertyNames.length - 1; ++i) {
					obj = getProperty(obj, propertyNames[i]);
					if (obj == null) {
						return null;
					}
				}
				PropertyFieldUtils fieldProp = new PropertyFieldUtils(obj);
				desc = (PropertyFieldDescriptor) fieldProp.classprops.get(propertyNames[(propertyNames.length - 1)]);
			}
		}

		if (desc == null)
			return null;
		PropertyField field = new PropertyField();
		field.setDesc(desc);
		field.setInstance(obj);
		return field;
	}

	public void setPropertyField(String name, Object value) throws Exception {
		PropertyField field = getPropertyFieldDefine(name);
		if(field == null) {
			field = getPropertyFieldDefine(name.toLowerCase());
			if (field == null) {
				field = getPropertyFieldDefine(name.toUpperCase());
				if(field == null){
					throw new Exception(objInstance.getClass()+ ":" + name + "属性不存在");
				}
			}
		}

		field.set(value);
	}

	public HashMap getClassPropMap() {
		return this.classprops;
	}

	public Object getInstance() {
		return this.objInstance;
	}

	public PropertyFieldDescriptor[] getFieldDescriptor() {
		PropertyFieldDescriptor[] props = new PropertyFieldDescriptor[this.classprops.size()];

		props = (PropertyFieldDescriptor[]) (PropertyFieldDescriptor[]) this.classprops.values().toArray(props);
		return props;
	}

	public PropertyFieldDescriptor getFieldDescriptor(String name) throws Exception {
		PropertyFieldDescriptor desc = (PropertyFieldDescriptor) this.classprops.get(name);

		if (desc == null) {
			throw new Exception(name);
		}

		return desc;
	}

	public Object getPropertyField(String name) throws Exception {
		PropertyField field = getPropertyFieldDefine(name);
		if (field == null) {
			throw new Exception(name);
		}

		return field.get();
	}

	public String toString() {
		StringBuffer sbuf = new StringBuffer();
		PropertyFieldDescriptor[] props = getFieldDescriptor();
		for (int i = 0; i < props.length; ++i) {
			sbuf.append(props[i].getPropertyName());
			sbuf.append("=\"");
			sbuf.append(props[i].get(this.objInstance));
			sbuf.append("\";");
		}

		return sbuf.toString();
	}

	private HashMap getClassPropertys(Class c) {
		Method[] methods = c.getMethods();
		Field[] fields = c.getFields();
		HashMap props = new HashMap();

		Method read = null;
		Method write = null;
		Method m;
		String setName;
		String propName;
		// int j;
		for (int i = 0; i < methods.length; ++i) {
			m = methods[i];
			if (Modifier.isPublic(m.getModifiers())) {
				setName = null;
				if (m.getName().startsWith("get")) {
					setName = m.getName().replaceFirst("get", "set");
				} else if (m.getName().startsWith("is")) {
					setName = m.getName().replaceFirst("is", "set");
				}

				if (setName != null) {
					read = m;
					write = null;
					propName = propertyName(setName.substring(3));
					for (int j = 0; j < methods.length; ++j) {
						m = methods[j];
						if ((!(Modifier.isPublic(m.getModifiers())))
								|| (!(m.getName().equals(setName))))
							continue;
						write = m;
					}

					if (write != null) {
						props.put(propName, new PropertyFieldDescriptor(propName, read, write));
					} else {
						boolean findField = false;
						for (int j = 0; j < fields.length; ++j) {
							Field f = fields[j];
							if ((!(Modifier.isPublic(f.getModifiers()))) || (!(f.getName().equals(propName))))
								continue;
							findField = true;
							props.put(propName, new PropertyFieldDescriptor(read, f));
							break;
						}

						if (!(findField)) {
							props.put(propName, new PropertyFieldDescriptor(propName, read, write));
						}
					}
				}

			}

		}

		for (int i = 0; i < fields.length; ++i) {
			Field f = fields[i];
			propName = f.getName();
			setName = "set" + capitalize(propName);

			if ((Modifier.isPublic(f.getModifiers()))
					&& (props.get(propName) == null)) {
				write = null;
				for (int j = 0; j < methods.length; ++j) {
					m = methods[j];
					if ((Modifier.isPublic(m.getModifiers()))
							&& (m.getName().equals(setName))) {
						write = m;
					}

					if (write != null) {
						props.put(propName, new PropertyFieldDescriptor(f,
								write));
					} else {
						props.put(propName, new PropertyFieldDescriptor(f));
					}
				}
			}
		}
		return props;
	}

	public static void setProperty(Object obj, String name, Object value) throws Exception {
		PropertyFieldUtils prop = new PropertyFieldUtils(obj);
		prop.setPropertyField(name, value);
	}

	public static boolean isByteAray(Class c) {
		if (c.isArray()) {
			Class pc = c.getComponentType();
			if (pc.equals(Byte.TYPE)) {
				return true;
			}
		}
		return false;
	}

	public static Object getProperty(Object obj, String name) throws Exception {
		if (obj == null) {
			return null;
		}
		PropertyFieldUtils prop = new PropertyFieldUtils(obj);
		return prop.getPropertyField(name);
	}

	public static String objectToString(Object obj) {
		PropertyFieldUtils prop = new PropertyFieldUtils(obj);
		return prop.toString();
	}

	public static String capitalize(String s) {
		if (s.length() == 0) {
			return s;
		}
		char[] chars = s.toCharArray();
		chars[0] = Character.toUpperCase(chars[0]);
		return new String(chars);
	}

	public static String propertyName(String s) {
		if (s.length() == 0) {
			return s;
		}
		char[] chars = s.toCharArray();
		chars[0] = Character.toLowerCase(chars[0]);
		return new String(chars);
	}

	public static Object coverTo(Object obj, Class toType) {
		if (obj == null) {
			return null;
		}

		String s;
		if ((toType.equals(Byte.TYPE)) || (toType.equals(Byte.class))) {
			if (Byte.class.isInstance(obj)) {
				return obj;
			}
			s = obj.toString();
			return new Byte(s);
		}

		if ((toType.equals(Character.TYPE)) || (toType.equals(Character.class))) {
			if (Character.class.isInstance(obj)) {
				return obj;
			}
			s = obj.toString();
			return new Character((char) Integer.parseInt(s));
		}

		if ((toType.equals(Short.TYPE)) || (toType.equals(Short.class))) {
			if (Short.class.isInstance(obj)) {
				return obj;
			}
			s = obj.toString();
			return new Short(s);
		}

		if ((toType.equals(Long.TYPE)) || (toType.equals(Long.class))) {
			if (Long.class.isInstance(obj)) {
				return obj;
			}
			s = obj.toString();
			return new Long(s);
		}

		if ((toType.equals(Integer.TYPE)) || (toType.equals(Integer.class))) {
			if (Integer.class.isInstance(obj)) {
				return obj;
			}
			s = obj.toString();
			return new Integer(s);
		}

		if ((toType.equals(Float.TYPE)) || (toType.equals(Float.class))) {
			if (Float.class.isInstance(obj)) {
				return obj;
			}
			s = obj.toString();
			return new Float(s);
		}

		if ((toType.equals(Double.TYPE)) || (toType.equals(Double.class))) {
			if (Double.class.isInstance(obj)) {
				return obj;
			}
			s = obj.toString();
			return new Double(s);
		}

		if (toType.equals(String.class)) {
			if (String.class.isInstance(obj)) {
				return obj;
			}
			if (Date.class.isInstance(obj)) {
				return DateUtil.DateTimeToISOStr((Date) obj);
			}
			return obj.toString();
		}

		if ((toType.equals(Boolean.TYPE)) || (toType.equals(Boolean.class))) {
			if (Boolean.class.isInstance(obj)) {
				return obj;
			}
			s = obj.toString();
			return new Boolean(s);
		}

		if (toType.equals(Date.class)) {
			if (Date.class.isInstance(obj)) {
				return new Date(((Date) obj).getTime() / 1000L * 1000L);
			}

			if (String.class.isInstance(obj)) {
				return DateUtil.paseDatetime((String) obj);
			}
			return null;
		}

		if (toType.equals(Timestamp.class)) {
			if (Date.class.isInstance(obj)) {
				return new Timestamp(((Date) obj).getTime() / 1000L * 1000L);
			}

			if (String.class.isInstance(obj)) {
				return new Timestamp(
						DateUtil.paseDatetime((String) obj).getTime() / 1000L * 1000L);
			}

			return null;
		}

		if(toType.equals(String[].class)){
			return (String[])obj;
		}

		return null;
	}

	public static Object assign(Object src, Object dest) {
		PropertyFieldUtils psrc = new PropertyFieldUtils(src);
		PropertyFieldUtils pdest = new PropertyFieldUtils(dest);
		PropertyFieldDescriptor[] props = psrc.getFieldDescriptor();
		for (int i = 0; i < props.length; ++i) {
			PropertyFieldDescriptor desc = (PropertyFieldDescriptor) pdest
					.getClassPropMap().get(props[i].getPropertyName());

			if (desc != null) {
				desc.set(dest, props[i].get(src));
			}
		}
		return dest;
	}
}